import ConfigTest.ZoneGraphObjHelper.getZoneGraphObj1
import ConfigTest.ZoneGraphObjHelper.getZoneGraphObj2
import ConfigTest.ZoneGraphObjHelper.getZoneGraphObj3
import ConfigTest.ZoneGraphObjHelper.getZoneGraphObj4
import ConfigTest.ZoneStateHelper.ZoneGraph
import ConfigTest.ZoneStateHelper.ZonePath
import ConfigTest.ZoneStateHelper.ZoneState
import ConfigTest.ZoneStateHelper.ZoneState.Companion.getZoneState
import ConfigTest.ZoneStateHelper.ZoneTransition.Companion.getZoneTransition
import helper.base.StateHelper.EventChoice
import helper.base.StateHelper.EventChoice.Companion.makeEventChoice
import helper.base.StateHelper.Graph
import helper.base.StateHelper.Path
import helper.base.StateHelper.State
import helper.base.StateHelper.Transition
import org.junit.Test

internal class ConfigTest {
    private object ZoneStateHelper {
        class ZoneState(
            override var name: String,
            val location: String,
            val cons: String,
            val reward: Double = 0.0,
        ) : State(
        ) {
            companion object {
                fun getZoneState(
                    name: String,
                    location: String,
                    cons: String,
                    reward: Double = 0.0,
                ): ZoneState {
                    return ZoneState(
                        name,
                        location,
                        cons,
                        reward,
                    )
                }
            }

            override fun clone(): ZoneState {
                return ZoneState(name, location, cons, reward)
            }

            override fun toString(): String {
                return "ZoneState(name='$name', location='$location', cons='$cons', reward=$reward)"
            }
        }

        class ZoneTransition(
            start: ZoneState,
            eventChoice: EventChoice,
            end: ZoneState,
        ) : Transition<ZoneState, EventChoice>(
            start,
            eventChoice,
            end,
        ) {
            companion object {
                fun getZoneTransition(
                    start: ZoneState,
                    eventChoice: EventChoice,
                    end: ZoneState,
                ): ZoneTransition {
                    return ZoneTransition(
                        start,
                        eventChoice,
                        end,
                    )
                }

                fun getZoneTransition(
                    start: ZoneState,
                    event: String,
                    end: ZoneState,
                ): ZoneTransition {
                    return ZoneTransition(
                        start,
                        makeEventChoice(event),
                        end,
                    )
                }
            }

            override fun clone(): ZoneTransition {
                return ZoneTransition(
                    start.clone(),
                    eventChoice.clone(),
                    end.clone(),
                )
            }
        }

        class ZonePath(
            pReward: Double = 1.0,
        ) : Path<ZoneTransition>(
            pReward,
        ) {
            override fun clone(): ZonePath {
                val newPath = ZonePath(this.pReward)
                this.map {
                    newPath.add(it.clone())
                }
                return newPath
            }

            override fun toString(): String {
                val sb = StringBuilder()
                sb.append("ZonePath(")
                this.map {
                    sb.append("(${it.start.name},${it.chosenEvent},${it.end.name})")
                    sb.append(",")
                }
                sb.append("pReward=${pReward}")
                sb.append(")")
                return sb.toString()
            }
        }

        /**
         * 不能有环路，人工判断
         */
        class ZoneGraph(
            start: ZoneState,
            ts: ArrayList<ZoneTransition>,
            finalStates: LinkedHashSet<ZoneState> = LinkedHashSet(),
        ) : Graph<ZoneState, EventChoice, ZoneTransition>(
            start,
            ts,
            finalStates,
        ) {
            fun dfsTouchType2(
                nowZT: ZoneTransition? = null,
                visited: LinkedHashSet<ZoneTransition> = LinkedHashSet(),
                nowPath: ZonePath = ZonePath(),
                pathList: ArrayList<ZonePath> = ArrayList(),
            ) {
                val isNowZoneTransitionNull = nowZT == null
                if (!isNowZoneTransitionNull) {
                    nowZT!!
                    val totalP = this.stateEventPLHM[nowZT.start]!!.values.sum()
                    val nowP = this.stateEventPLHM[nowZT.start]!![nowZT.eventChoice]!!
                    nowPath.add(nowZT, nowP / totalP)
                }
                val nowState: ZoneState = if (isNowZoneTransitionNull) {
                    start
                } else {
                    nowZT!!.end
                }
                val isEnd = this.finalStates.contains(nowState)
                if (isEnd) {
                    nowPath.pReward *= nowState.reward
                    pathList.add(nowPath)
                    return
                }
                if (!isNowZoneTransitionNull) {
                    visited.add(nowZT!!)
                }
                stateEventTLHM[nowState]?.map { (_, a3) ->
                    if (visited.contains(a3)) return@map
                    val newPath = nowPath.clone()
                    dfsTouchType2(
                        a3,
                        visited,
                        newPath,
                        pathList,
                    )
                }
            }
        }
    }

    private object ZoneGraphObjHelper {
        fun getZSList1(): ArrayList<ZoneState> {
            val zone1 = getZoneState("z1", "l0", "x=y")
            val zone2 = getZoneState("z2", "l1", "0<=x<1 and x<=y")
            val zone3 = getZoneState("z3", "l2", "0<=x<1 and 0<=y")
            val zone4 = getZoneState("z4", "l3", "0<=x and 0<=y")
            val zone5 = getZoneState("z5", "l0", "2<x and 2<y")
            return arrayListOf(
                zone1,
                zone2,
                zone3,
                zone4,
                zone5,
            )
        }

        fun getZSList2(): ArrayList<ZoneState> {
            val zone1 = getZoneState("z1", "l0", "x=y")
            val zone2 = getZoneState("z2", "l1", "0<=x<1 and x<=y")
            val zone3 = getZoneState("z3", "l2", "0<=x<1 and 0<=y")
            val zone4 = getZoneState("z4", "l3", "0<=x and 0<=y", 60.0)
            val zone5 = getZoneState("z5", "l0", "2<x and 2<y", 50.0)
            return arrayListOf(
                zone1,
                zone2,
                zone3,
                zone4,
                zone5,
            )
        }

        fun getZoneGraphObj1(): ZoneGraph {
            val zsList = getZSList1()
            val (zone1, zone2, zone3, zone4, zone5) = zsList
            val ts = arrayListOf(
                getZoneTransition(zone1, "t1t2", zone2),
                getZoneTransition(zone2, "t2t3", zone3),
                getZoneTransition(zone3, "t3t4", zone4),
                getZoneTransition(zone4, "t4t4", zone4),
                getZoneTransition(zone4, "t4t5", zone5),
                getZoneTransition(zone5, "t5t2", zone2),
            )
            return ZoneGraph(zone1, ts)
        }

        fun getZoneGraphObj2(): ZoneGraph {
            val zsList = getZSList1()
            val (zone1, zone2, zone3, zone4, zone5) = zsList
            val ts = arrayListOf(
                getZoneTransition(zone1, "t1t2", zone2),
                getZoneTransition(zone2, "t2t3", zone3),
                getZoneTransition(zone2, "t2t4", zone4),
                getZoneTransition(zone3, "t3t4", zone4),
                getZoneTransition(zone4, "t4t4", zone4),
                getZoneTransition(zone4, "t4t5", zone5),
            )
            return ZoneGraph(zone1, ts)
        }

        fun getZoneGraphObj3(): ZoneGraph {
            val zsList = getZSList2()
            val (zone1, zone2, zone3, zone4, zone5) = zsList
            val ts = arrayListOf(
                getZoneTransition(zone1, "t1t2", zone2),
                getZoneTransition(zone2, "t2t3", zone3),
                getZoneTransition(zone2, "t2t4", zone4),
                getZoneTransition(zone2, "t2t5", zone5),
                getZoneTransition(zone3, "t3t4", zone4),
            )

            return ZoneGraph(
                zone1,
                ts,
                finalStates = linkedSetOf(
                    zone4,
                    zone5,
                ),
            )
        }

        fun getZoneGraphObj4(): ZoneGraph {
            val zsList = getZSList2()
            val (zone1, zone2, zone3, zone4, zone5) = zsList
            val t2t4 = getZoneTransition(zone2, "t2t4", zone4)
            val t2t5 = getZoneTransition(zone2, "t2t5", zone5)
            val ts = arrayListOf(
                getZoneTransition(zone1, "t1t2", zone2),
                getZoneTransition(zone2, "t2t3", zone3),
                t2t4,
                t2t5,
                getZoneTransition(zone3, "t3t4", zone4),
            )
            val zoneGraph = ZoneGraph(
                zone1,
                ts,
                finalStates = linkedSetOf(
                    zone4,
                    zone5,
                ),
            )
            zoneGraph.stateEventPLHM.let {
                //概率生效
                it[zone2]!![t2t4.eventChoice] = 2.0
                it[zone2]!![t2t5.eventChoice] = 2.0
            }
            return zoneGraph
        }
    }

    @Test
    fun t1t1() {
        val zoneGraph = getZoneGraphObj1()
        val rs = zoneGraph.stateEventTLHM.touchPrintlnType1()
        println(rs)
        assert(
            rs.trim() == """
            z1,t1t2,z2
            z2,t2t3,z3
            z3,t3t4,z4
            z4,t4t4,z4
            z4,t4t5,z5
            z5,t5t2,z2
        """.trimIndent()
        )
    }

    @Test
    fun t1t2() {
        val zoneGraph = getZoneGraphObj1()
        val rs = zoneGraph.dfsTouchType1()
        println(rs)
        assert(
            rs.trim() == """
z1
	t1t2 z2
		t2t3 z3
			t3t4 z4
				t4t4 z4
					t4t5 z5
						t5t2 z2
        """.trimIndent()
        )
    }

    @Test
    fun t2t1() {
        val zoneGraph = getZoneGraphObj2()
        val rs = zoneGraph.dfsTouchType1()
        println(rs)
        assert(
            rs.trim() == """
z1
	t1t2 z2
		t2t3 z3
			t3t4 z4
				t4t4 z4
					t4t5 z5
		t2t4 z4
        """.trimIndent()
        )
    }

    @Test
    fun t3() {
        val zoneGraph = getZoneGraphObj3()
        val rs1 = zoneGraph.stateEventTLHM.touchPrintlnType1()
        val rs2 = zoneGraph.stateEventPLHM.touchPrintlnType1()
        val pathList = ArrayList<ZonePath>()
        zoneGraph.dfsTouchType2(
            pathList = pathList,
        )
        val sb3 = StringBuilder()
        pathList.map {
            sb3.append("$it\n")
        }
        val rs3 = sb3.toString()
        println(rs1)
        println(rs2)
        println(rs3)

        assert(
            rs1.trim() == """
            z1,t1t2,z2
            z2,t2t3,z3
            z2,t2t4,z4
            z2,t2t5,z5
            z3,t3t4,z4
        """.trimIndent()
        )
        assert(
            rs2.trim() == """
            z1,t1t2,1.0
            z2,t2t3,1.0
            z2,t2t4,1.0
            z2,t2t5,1.0
            z3,t3t4,1.0
        """.trimIndent()
        )
        assert(
            rs3.trim() == """
            ZonePath((z1,t1t2,z2),(z2,t2t3,z3),(z3,t3t4,z4),pReward=20.0)
            ZonePath((z1,t1t2,z2),(z2,t2t4,z4),pReward=20.0)
            ZonePath((z1,t1t2,z2),(z2,t2t5,z5),pReward=16.666666666666664)
        """.trimIndent()
        )
    }

    @Test
    fun t4() {
        val zoneGraph = getZoneGraphObj4()
        val rs1 = zoneGraph.stateEventTLHM.touchPrintlnType1()
        val rs2 = zoneGraph.stateEventPLHM.touchPrintlnType1()
        val pathList: ArrayList<ZonePath> = ArrayList()
        zoneGraph.dfsTouchType2(
            pathList = pathList,
        )
        val sb3 = StringBuilder()
        pathList.map {
            sb3.append("$it\n")
        }
        val rs3 = sb3.toString()
        println(rs1)
        println(rs2)
        println(rs3)

        assert(
            rs1.trim() == """
            z1,t1t2,z2
            z2,t2t3,z3
            z2,t2t4,z4
            z2,t2t5,z5
            z3,t3t4,z4
        """.trimIndent()
        )
        assert(
            rs2.trim() == """
            z1,t1t2,1.0
            z2,t2t3,1.0
            z2,t2t4,2.0
            z2,t2t5,2.0
            z3,t3t4,1.0
        """.trimIndent()
        )
        assert(
            rs3.trim() == """
            ZonePath((z1,t1t2,z2),(z2,t2t3,z3),(z3,t3t4,z4),pReward=12.0)
            ZonePath((z1,t1t2,z2),(z2,t2t4,z4),pReward=24.0)
            ZonePath((z1,t1t2,z2),(z2,t2t5,z5),pReward=20.0)
        """.trimIndent()
        )
    }

    @Test
    fun t5() {
        val zoneGraph = getZoneGraphObj3()
        val sb1 = StringBuilder()
        zoneGraph.stateLHS.map {
            sb1.append("$it\n")
            sb1.append("${zoneGraph.stateLHS.indexOf(it)}\n")
        }
        val rs1 = sb1.toString()
        println(rs1)
        assert(
            rs1.trim() == """
            ZoneState(name='z1', location='l0', cons='x=y', reward=0.0)
            0
            ZoneState(name='z2', location='l1', cons='0<=x<1 and x<=y', reward=0.0)
            1
            ZoneState(name='z3', location='l2', cons='0<=x<1 and 0<=y', reward=0.0)
            2
            ZoneState(name='z4', location='l3', cons='0<=x and 0<=y', reward=60.0)
            3
            ZoneState(name='z5', location='l0', cons='2<x and 2<y', reward=50.0)
            4
        """.trimIndent()
        )
    }
}